/*
 * Copyright (c) 2012 Mockito contributors
 * This program is made available under the terms of the MIT License.
 */

package org.mockito.internal.creation;

import com.google.dexmaker.stock.ProxyBuilder;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import org.mockito.internal.invocation.Invocation;
import org.mockito.internal.invocation.MockitoMethod;
import org.mockito.internal.invocation.realmethod.RealMethod;
import org.mockito.internal.progress.SequenceNumber;
import org.mockito.internal.util.ObjectMethodsGuru;
import org.mockito.plugins.MockitoInvocationHandler;

/**
 * Handles proxy method invocations to dexmaker's InvocationHandler by calling
 * a MockitoInvocationHandler.
 */
public final class AndroidMethodInterceptorFilter implements InvocationHandler {
    private final MockitoInvocationHandler handler;
    private final ObjectMethodsGuru objectMethodsGuru = new ObjectMethodsGuru();

    public AndroidMethodInterceptorFilter (MockitoInvocationHandler handler) {
        this.handler = handler;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (objectMethodsGuru.isEqualsMethod(method)) {
            return proxy == args[0];
        } else if (objectMethodsGuru.isHashCodeMethod(method)) {
            return System.identityHashCode(proxy);
        }
        
        if (args == null) {
            throw new IllegalArgumentException();
        }
        

        ProxiedMethod proxiedMethod = new ProxiedMethod(method);
        return handler.handle(new Invocation(proxy, proxiedMethod, args, SequenceNumber.next(),
                proxiedMethod));
    }

    public MockitoInvocationHandler getHandler() {
        return handler;
    }

    private static class ProxiedMethod implements MockitoMethod, RealMethod {
        private final Method method;

        public ProxiedMethod(Method method) {
            this.method = method;
        }

        public String getName() {
            return method.getName();
        }

        public Class<?> getReturnType() {
            return method.getReturnType();
        }

        public Class<?>[] getParameterTypes() {
            return method.getParameterTypes();
        }

        public Class<?>[] getExceptionTypes() {
            return method.getExceptionTypes();
        }

        public boolean isVarArgs() {
            return method.isVarArgs();
        }

        public Method getJavaMethod() {
            return method;
        }

        public Object invoke(Object target, Object[] arguments) throws Throwable {
            return ProxyBuilder.callSuper(target, method, arguments);
        }
    }
}
